﻿using System;
using Sunny.Core.Exception;

namespace Sunny.Core.DefinedData
{
    [Serializable]
    public struct SMoney : IComparable<SMoney>
    {
        private readonly decimal? _money;

        public SMoney(decimal? value)
        {
            this._money = value;
        }

        #region Implicit Operator WebEziMoney

        public static implicit operator SMoney(string value)
        {
            if (String.IsNullOrEmpty(value))
            {
                return new SMoney(null);
            }
            else
            {
                decimal money;

                string newMoney = value.Replace("$", string.Empty);
                
                if (newMoney.Equals("NaN.00"))
                {
                    throw new DefinedDataException("The $NaN.00 which you input is invalid.", null);
                }
                if (decimal.TryParse(newMoney, out money))
                {
                    return new SMoney(money);
                }
                else
                {
                    throw new DefinedDataException("The source value is " + value);
                }
            }
        }

        public static implicit operator SMoney(decimal? value)
        {
            return new SMoney(value);
        }

        #endregion

        #region Implicit Operator Other

        public static implicit operator string(SMoney value)
        {
            if(value._money.HasValue)
            {
                return value._money.Value.ToString();
            }
            else
            {
                return string.Empty;
            }
        }

        public static implicit operator decimal(SMoney value)
        {
            if (!value._money.HasValue)
            {
                return 0;
            }

            return value._money.Value;
        }

        public static implicit operator decimal?(SMoney value)
        {
            return value._money;
        }

        #endregion

        #region Implicit Operator

        public static decimal operator +(SMoney x, SMoney y)
        {
            decimal newX = 0;
            decimal newY = 0;
            if(!x.IsNull())
            {
                newX = x._money.Value;
            }
            if(!y.IsNull())
            {
                newY = y._money.Value;
            }

            return newX + newY;
        }

        public static decimal operator -(SMoney x, SMoney y)
        {
            decimal newX = 0;
            decimal newY = 0;
            if (!x.IsNull())
            {
                newX = x._money.Value;
            }
            if (!y.IsNull())
            {
                newY = y._money.Value;
            }

            return newX - newY;
        }

        public static decimal operator *(SMoney x, SMoney y)
        {
            decimal newX = 0;
            decimal newY = 0;
            if (!x.IsNull())
            {
                newX = x._money.Value;
            }
            if (!y.IsNull())
            {
                newY = y._money.Value;
            }

            return newX * newY;
        }

        public static decimal operator *(SMoney x, int y)
        {
            decimal newX = 0;
            if (!x.IsNull())
            {
                newX = x._money.Value;
            }

            return newX * y;
        }

        public static decimal operator *(SMoney x, double y)
        {
            decimal newX = 0;
            if (!x.IsNull())
            {
                newX = x._money.Value;
            }

            return newX * decimal.Parse(y.ToString());
        }
        public static decimal operator -(SMoney x, double y)
        {
            decimal newX = 0;
            if (!x.IsNull())
            {
                newX = x._money.Value;
            }

            return newX - decimal.Parse(y.ToString());
        }
        public static decimal operator -(double x, SMoney y)
        {
            decimal newY = 0;
            if (!y.IsNull())
            {
                newY = y._money.Value;
            }

            return decimal.Parse(x.ToString()) - newY;
        }

        public static decimal operator /(SMoney x, SMoney y)
        {
            decimal newX = 0;
            decimal newY = 0;
            if (!x.IsNull())
            {
                newX = x._money.Value;
            }
            if (!y.IsNull())
            {
                newY = y._money.Value;
            }

            return newX / newY;
        }

        public static bool operator ==(SMoney x, SMoney y)
        {
            return x._money == y._money;
        }

        public static bool operator !=(SMoney x, SMoney y)
        {
            return x._money != y._money;
        }

        public static bool operator ==(SMoney x, int y)
        {
            return x._money == decimal.Parse(y.ToString());
        }

        public static bool operator !=(SMoney x, int y)
        {
            return x._money != decimal.Parse(y.ToString());
        }

        public static bool operator ==(SMoney x, double y)
        {
            return x._money == decimal.Parse(y.ToString());
        }

        public static bool operator !=(SMoney x, double y)
        {
            return x._money != decimal.Parse(y.ToString());
        }

        public static bool operator >(SMoney x, double y)
        {
            return x._money > decimal.Parse(y.ToString());
        }

        public static bool operator <(SMoney x, double y)
        {
            return x._money < decimal.Parse(y.ToString());
        }

        #endregion

        /// <summary>
        /// Return the value is null
        /// </summary>
        /// <returns>The value is null</returns>
        public bool IsNull()
        {
            return !_money.HasValue;
        }

        public override string ToString()
        {
            if(_money.HasValue)
            {
                return _money.Value.ToString("C");
            }
            else
            {
                return decimal.Parse("0").ToString("C");
            }
        }

        public string ToNoRoundingString()
        {
            if (_money.HasValue)
            {
                var tmpValue = (Math.Truncate(_money.Value*100)/100).ToString();
                return decimal.Parse(tmpValue).ToString("C");
            }
            else
            {
                return decimal.Parse("0").ToString("C");
            }
        }

        #region IComparable<WebEziMoney> Members

        public int CompareTo(SMoney other)
        {
            if (_money.HasValue)
            {
                return _money.Value.CompareTo(other._money);
            }
            else if (!other._money.HasValue)
            {
                return 0;
            }
            else
            {
                return -1;
            }
        }

        #endregion

        public bool Equals(SMoney obj)
        {
            return obj._money.Equals(_money);
        }

        public override bool Equals(object obj)
        {
            if (obj.GetType() != typeof (SMoney)) return false;
            return Equals((SMoney) obj);
        }

        public override int GetHashCode()
        {
            return (_money.HasValue ? _money.Value.GetHashCode() : 0);
        }
    }
}
